/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the IncrementalParser package.
 *  
 *  The IncrementalParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.osdtsystem.incparser.graphs;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class GraphOverlayMoveEdge extends GraphOverlay implements GraphOverlayEdgeTypeInterface {
    int layer;
    int innode;
    int newoutnode;
    int newtype;
    int oldoutnode;
    int oldtype;
    AbstractLocalGraph inNodeGraph;
    AbstractLocalGraph newOutNodeGraph;
    AbstractLocalGraph oldOutNodeGraph;

    GraphOverlayMoveEdge(final AbstractGraph parentGraph, final int layer,
            final int innode, final int oldoutnode, final int oldtype,
            final int newoutnode, final int newtype) {
        // Call parent constructor and initialize variables
        super(parentGraph);
        this.layer = layer;
        setMoveEdge(innode, oldoutnode, oldtype, newoutnode, newtype);
    }

    public void setMoveEdge(final int innode, final int oldoutnode, final int oldtype,
            final int newoutnode, final int newtype) {
        // Clear all modifications to the parent graph and create initial local graphs
        if (inNodeGraph == null) {
            // Create new local graphs
            inNodeGraph = super._localGraphRW(innode, layer).clone();
            newOutNodeGraph = super._localGraphRW(newoutnode, layer).clone();
            oldOutNodeGraph = oldoutnode == newoutnode
                    ? newOutNodeGraph : super._localGraphRW(oldoutnode, layer).clone();
        } else {
            // Change existing local graphs
            clearModifications();
            int innodePrev = this.innode;
            int outnodePrev = this.newoutnode;
            int typePrev = this.newtype;
            int oldoutnodePrev = this.oldoutnode;
            int oldtypePrev = this.oldtype;
            this.inNodeGraph.removeInEdge(Helper.pack(outnodePrev, typePrev));
            this.newOutNodeGraph.removeOutEdge(Helper.pack(innodePrev, typePrev));
            this.inNodeGraph.addInEdge(Helper.pack(oldoutnodePrev, oldtypePrev));
            this.oldOutNodeGraph.addOutEdge(Helper.pack(innodePrev, oldtypePrev));
        }

        // Add new edge
        this.innode = innode;
        this.oldoutnode = oldoutnode;
        this.oldtype = oldtype;
        this.newoutnode = newoutnode;
        this.newtype = newtype;
        this.inNodeGraph.removeInEdge(Helper.pack(oldoutnode, oldtype));
        this.oldOutNodeGraph.removeOutEdge(Helper.pack(innode, oldtype));
        this.inNodeGraph.addInEdge(Helper.pack(newoutnode, newtype));
        this.newOutNodeGraph.addOutEdge(Helper.pack(innode, newtype));

        // Notify listeners
        baseGraph._notifyEdgeMoved(this, layer, innode, oldoutnode, oldtype, newoutnode, newtype);
    }

    public void setEdgeType(final int newNewtype) {
        int oldNewtype = this.newtype;
        inNodeGraph.removeInEdge(Helper.pack(newoutnode, oldNewtype));
        newOutNodeGraph.removeOutEdge(Helper.pack(innode, oldNewtype));
        inNodeGraph.addInEdge(Helper.pack(newoutnode, newNewtype));
        newOutNodeGraph.addOutEdge(Helper.pack(innode, newNewtype));
        baseGraph._notifyEdgeTypeChanged(this, layer, innode, newoutnode, oldNewtype, newNewtype);
    }

    @Override
    public LocalGraph localGraph(final int node, final int layer) {
        if (node == innode && layer == this.layer)
            return inNodeGraph;
        if (node == newoutnode && layer == this.layer)
            return newOutNodeGraph;
        if (node == oldoutnode && layer == this.layer)
            return oldOutNodeGraph;
        return
            super.localGraph(node, layer);
    }

    @Override
    void _writeGraphChangesToBaseGraphPreprocess() {
        baseGraph.moveEdge(layer, innode, oldoutnode, oldtype, newoutnode, newtype);
    }

    public void setBestEdgeType(int types) {
        double bestScore = Double.NEGATIVE_INFINITY;
        int bestType = 0;
        for (int type = 0; type < types; ++type) {
            setEdgeType(type);
            double newscore = score();
            if (newscore > bestScore) {
                bestScore = newscore;
                bestType = type;
            }
        }
        setEdgeType(bestType);
    }

    public int edgeType() {
        return newtype;
    }
}
